/**
 * @file scanner-inl.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2024-03-30
 * 
 * @copyright Copyright (c) 2024
 * 
 */
#ifndef __JSASM_SCANNER_INL_H__
#define __JSASM_SCANNER_INL_H__

#include "scanner.h"
#include "src/parsing/scanner.h"

#define KEYWORDS(KEYWORD_GROUP, KEYWORD)                            \
    KEYWORD_GROUP('a')                                              \
    KEYWORD("async", Token::ASYNC)                                  \
    KEYWORD("await", Token::AWAIT)                                  \
    KEYWORD_GROUP('a')                                              \
    KEYWORD("break", Token::BREAK)                                  \
    KEYWORD_GROUP('c')                                              \
    KEYWORD("case", Token::CASE)                                    \
    KEYWORD("catch", Token::CATCH)                                  \
    KEYWORD("class", Token::CLASS)                                  \
    KEYWORD("const", Token::CONST)                                  \
    KEYWORD("continue", Token::CONTINUE)                            \
    KEYWORD_GROUP('d')                                              \
    KEYWORD("debugger", Token::DEBUGGER)                            \
    KEYWORD("default", Token::DEFAULT)                              \
    KEYWORD("delete", Token::DELETE)                                \
    KEYWORD("do", Token::DO)                                        \
    KEYWORD_GROUP('e')                                              \
    KEYWORD("else", Token::ELSE)                                    \
    KEYWORD("enum", Token::ENUM)                                    \
    KEYWORD("export", Token::EXPORT)                                \
    KEYWORD("extends", Token::EXTENDS)                              \
    KEYWORD_GROUP('f')                                              \
    KEYWORD("false", Token::FALSE_LITERAL)                          \
    KEYWORD("finally", Token::FINALLY)                              \
    KEYWORD("for", Token::FOR)                                      \
    KEYWORD("function", Token::FUNCTION)                            \
    KEYWORD_GROUP('g')                                              \
    KEYWORD("get", Token::GET)                                      \
    KEYWORD_GROUP('i')                                              \
    KEYWORD("if", Token::IF)                                        \
    KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD)       \
    KEYWORD("import", Token::IMPORT)                                \
    KEYWORD("in", Token::IN)                                        \
    KEYWORD("instanceof", Token::INSTANCEOF)                        \
    KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD)        \
    KEYWORD_GROUP('l')                                              \
    KEYWORD("let", Token::LET)                                      \
    KEYWORD_GROUP('n')                                              \
    KEYWORD("new", Token::NEW)                                      \
    KEYWORD("null", Token::NULL_LITERAL)                            \
    KEYWORD_GROUP('p')                                              \
    KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD)          \
    KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD)          \
    KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD)        \
    KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD)           \
    KEYWORD_GROUP('r')                                              \
    KEYWORD("return", Token::RETURN)                                \
    KEYWORD_GROUP('s')                                              \
    KEYWORD("set", Token::SET)                                      \
    KEYWORD("static", Token::STATIC)                                \
    KEYWORD("super", Token::SUPER)                                  \
    KEYWORD("switch", Token::SWITCH)                                \
    KEYWORD_GROUP('t')                                              \
    KEYWORD("this", Token::THIS)                                    \
    KEYWORD("throw", Token::THROW)                                  \
    KEYWORD("true", Token::TRUE_LITERAL)                            \
    KEYWORD("try", Token::TRY)                                      \
    KEYWORD("typeof", Token::TYPEOF)                                \
    KEYWORD_GROUP('v')                                              \
    KEYWORD("var", Token::VAR)                                      \
    KEYWORD("void", Token::VOID)                                    \
    KEYWORD_GROUP('w')                                              \
    KEYWORD("while", Token::WHILE)                                  \
    KEYWORD("with", Token::WITH)                                    \
    KEYWORD_GROUP('y')                                              \
    KEYWORD("yield", Token::YIELD)

constexpr bool IsKeywordStart(char c) {
#define KEYWORD_GROUP_CHECK(ch) c == ch ||
#define KEYWORD_CHECK(keyword, token)
    return KEYWORDS(KEYWORD_GROUP_CHECK, KEYWORD_CHECK) /* || */ false;
#undef KEYWORD_GROUP_CHECK
#undef KEYWORD_CHECK
}

static_assert(sizeof(Token::Value) == 1);

inline constexpr int AsciiAlphaToLower(base::uc32 c) { return c | 0x20; }

inline constexpr bool IsCarriageReturn(base::uc32 c) { return c == 0x000D; }

inline constexpr bool IsLineFeed(base::uc32 c) { return c == 0x000A; }

inline constexpr bool IsDecimalDigit(base::uc32 c) {
  // ECMA-262, 3rd, 7.8.3 (p 16)
  return base::IsInRange(c, '0', '9');
}

inline constexpr bool IsAlphaNumeric(base::uc32 c) {
  return base::IsInRange(AsciiAlphaToLower(c), 'a', 'z') || IsDecimalDigit(c);
}

inline constexpr bool IsAsciiIdentifier(base::uc32 c) {
  return IsAlphaNumeric(c) || c == '$' || c == '_';
}

inline constexpr bool IsHexDigit(base::uc32 c) {
  // ECMA-262, 3rd, 7.6 (p 15)
  return IsDecimalDigit(c) || base::IsInRange(AsciiAlphaToLower(c), 'a', 'f');
}

constexpr Token::Value GetOneCharToken(char c) {
    // clang-format off
    return
        c == '(' ? Token::LPAREN :
        c == ')' ? Token::RPAREN :
        c == '{' ? Token::LBRACE :
        c == '}' ? Token::RBRACE :
        c == '[' ? Token::LBRACK :
        c == ']' ? Token::RBRACK :
        c == '?' ? Token::CONDITIONAL :
        c == ':' ? Token::COLON :
        c == ';' ? Token::SEMICOLON :
        c == ',' ? Token::COMMA :
        c == '.' ? Token::PERIOD :
        c == '|' ? Token::BIT_OR :
        c == '&' ? Token::BIT_AND :
        c == '^' ? Token::BIT_XOR :
        c == '~' ? Token::BIT_NOT :
        c == '!' ? Token::NOT :
        c == '<' ? Token::LT :
        c == '>' ? Token::GT :
        c == '%' ? Token::MOD :
        c == '=' ? Token::ASSIGN :
        c == '+' ? Token::ADD :
        c == '-' ? Token::SUB :
        c == '*' ? Token::MUL :
        c == '/' ? Token::DIV :
        c == '#' ? Token::PRIVATE_NAME :
        c == '"' ? Token::STRING :
        c == '\'' ? Token::STRING :
        c == '`' ? Token::TEMPLATE_SPAN :
        c == '\\' ? Token::IDENTIFIER :
        // Whitespace or line terminator
        c == ' ' ? Token::WHITESPACE :
        c == '\t' ? Token::WHITESPACE :
        c == '\v' ? Token::WHITESPACE :
        c == '\f' ? Token::WHITESPACE :
        c == '\r' ? Token::WHITESPACE :
        c == '\n' ? Token::WHITESPACE :
        // IsDecimalDigit must be tested before IsAsciiIdentifier
        IsDecimalDigit(c) ? Token::NUMBER :
        IsAsciiIdentifier(c) ? Token::IDENTIFIER :
        Token::ILLEGAL;
  // clang-format on
}

// Table of one-character tokens, by character (0x00..0x7F only).
static const constexpr Token::Value one_char_tokens[128] = {
#define CALL_GET_SCAN_FLAGS(N) GetOneCharToken(N),
    INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
#undef CALL_GET_SCAN_FLAGS
};

#undef KEYWORDS


Token::Value Scanner::ScanSingleToken() {
    Token::Value token;
    std::cout << "Scanner::ScanSingleToken" << std::endl;
    do {
      next().location.beg_pos = source_pos();
      std::cout << c0_ << std::endl;
      if (JSASM_LIKELY(static_cast<unsigned>(c0_) <= kMaxAscii)) {
        token = one_char_tokens[c0_];
        std::cout << token << std::endl;
        switch(token) {
          
        }
      }

    } while(token == Token::WHITESPACE);
    return token;
}

void Scanner::Scan(TokenDesc* next_desc) {
  std::cout << "Scanner::Scan" << std::endl;
  next_desc->token = ScanSingleToken();
}

void Scanner::Scan() { Scan(next_); }
#endif // __JSASM_SCANNER_INL_H__